package org.rambadger.command.auth;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.sun.jersey.core.util.Base64;
import org.hibernate.validator.constraints.NotEmpty;
import org.joda.time.DateTime;
import org.mongojack.Id;
import org.mongojack.MongoCollection;
import org.rambadger.command.axon.MongoIndexes;
import org.rambadger.command.validation.UrlIdentifier;

import javax.validation.constraints.NotNull;
import java.security.SecureRandom;

import static org.rambadger.command.axon.MongoIndexes.Index;
import static org.rambadger.command.axon.MongoIndexes.Key;

/**
 * Authentication token consisting of random 'key' value that is compared to cookie contents, an expiration date, and the username of the user that the token belongs to.
 *
 * AuthTokens are currently not under event sourcing.
 */
@MongoCollection(name = "login_tokens")
@MongoIndexes({
        @Index(name = "username_index", keys = @Key(name="username")),
        @Index(name = "expiration_index", keys = @Key(name="expiration"))
})
public class AuthToken {
    @Id @NotEmpty
    @JsonProperty String key;

    @UrlIdentifier @NotNull
    @JsonProperty String username;

    @NotEmpty
    @JsonProperty DateTime expiration;

    AuthToken() {}

    /**
     * Instantiates a new Auth token.
     *
     * @param key the {@value #KEY_BYTE_LENGTH} byte, base64 encoded, key that will be compared against cookie contents
     * @param username the username/id of the user associated with the token
     * @param expiration the expiration date of the token
     */
    public AuthToken(String key, String username, DateTime expiration) {
        this.key = key;
        this.username = username;
        this.expiration = expiration;
    }

    /**
     * @return the key
     */
    public String getKey() {
        return key;
    }

    /**
     * @return the username
     */
    public String getUsername() {
        return username;
    }

    /**
     * @return the expiration date
     */
    public DateTime getExpiration() {
        return expiration;
    }

    /**
     * @return whether we are currently passed the expiration date
     */
    public boolean isExpired() {
        return expiration.isBeforeNow();
    }

    /**
     * Dispenses a new login token for the specified username
     *
     * @param username the username
     * @return the new token with that username
     */
    public static AuthToken getNewTokenForUser(String username) {
        String key = makeLoginKey();
        DateTime expiration = DateTime.now().plusWeeks(2);
        return new AuthToken(key, username, expiration);
    }

    /**
     * The length, in bytes, of the keys before base64 encoding
     */
    public static final int KEY_BYTE_LENGTH = 32;

    /**
     * Generate a new random base64 encoded key.
     *
     * @return the generated key
     */
    public static String makeLoginKey() {
        byte bytes[] = new byte[KEY_BYTE_LENGTH];
        SecureRandom sr = new SecureRandom();
        sr.nextBytes(bytes);
        return new String(Base64.encode(bytes));
    }
}
